package com.kun.lagou.seckillservice.consumer;

import com.kun.lagou.commodity.api.CommodityApi;
import com.kun.lagou.order.api.OrderApi;
import com.kun.lagou.order.api.dto.OrderInfo;
import com.kun.lagou.seckillservice.dao.SeckillActivityDao;
import com.kun.lagou.seckillservice.dao.SeckillOrderDao;
import com.kun.lagou.seckillservice.model.SeckillOrder;
import lombok.Builder;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.rocketmq.spring.annotation.RocketMQMessageListener;
import org.apache.rocketmq.spring.core.RocketMQListener;
import org.apache.rocketmq.spring.core.RocketMQTemplate;
import org.springframework.messaging.support.GenericMessage;
import org.springframework.stereotype.Component;
import org.springframework.transaction.support.TransactionTemplate;

import java.util.Map;

import static com.kun.lagou.seckillservice.consumer.CreateSeckillOrderConsumer.TOPIC;

/**
 * @author Administrator
 */
@Slf4j
@Component
@RequiredArgsConstructor
@RocketMQMessageListener(topic = TOPIC, consumerGroup = "create-seckill-order-consumer")
public class CreateSeckillOrderConsumer implements RocketMQListener<CreateSeckillOrderConsumer.CreateSeckillOrderMessage> {
    public static final String TOPIC = "create-seckill-order";
    private final TransactionTemplate transactionTemplate;
    private final SeckillOrderDao seckillOrderDao;
    private final OrderApi orderApi;
    private final SeckillActivityDao seckillActivityDao;
    private final CommodityApi commodityApi;
    private final RocketMQTemplate rocketMQTemplate;

    //分布式事务：事务消息；TCC；saga；xa（jta）；at；最大努力通知；本地消息表

    @Override
    public void onMessage(CreateSeckillOrderMessage message) {
        createOrder2(message);
    }

    /**
     * 方案二
     * @param message
     */
    private void createOrder2(CreateSeckillOrderMessage message) {
        var activity = seckillActivityDao.findById(message.acitvityId).get();
        var commodity = commodityApi.getById(activity.getCommodityId());
        var order = OrderInfo.builder()
                .userId(message.userId)
                .commodityId(activity.getCommodityId())
                .biz("seckill")
                .params(Map.of("activityId", activity.getId()))
                .outOrderId(activity.getId() + "" + message.userId)
                .oldPrice(commodity.getPrice())
                .price(activity.getPrice())
                .build();
        rocketMQTemplate.sendMessageInTransaction("create-order",
                new GenericMessage<>(order), message);

    }

    /**
     * 方案一
     * @param message
     */
    private void createOrder1(CreateSeckillOrderMessage message) {
        //为什么不直接在订单服务里消费？
        //一般秒杀团队和订单团队是分开的，所以尽可能在秒杀系统维护一套完整的数据
        var seckillOrder = transactionTemplate.execute(s -> {
            if(seckillOrderDao.findByUserIdAndActivityId(message.userId, message.acitvityId).isEmpty()) {
                //也可以用乐观锁
                return seckillOrderDao.save(SeckillOrder.builder()
                        .userId(message.userId)
                        .activityId(message.acitvityId)
                        .build());
            }
            return null;
        });
        if(seckillOrder != null) {
            //对接订单服务 or 订单中台，走后面的流程
            var activity = seckillActivityDao.findById(message.acitvityId).get();
            var commodity = commodityApi.getById(activity.getCommodityId());
            orderApi.createOrder(OrderInfo.builder()
                    .userId(message.userId)
                    .commodityId(activity.getCommodityId())
                    .biz("seckill")
                    .params(Map.of("activityId", activity.getId()))
                    .outOrderId(activity.getId() + "" + message.userId)
                    .oldPrice(commodity.getPrice())
                    .price(activity.getPrice())
                    .build());
            //这里还需要做一个对账功能，保证秒杀系统和订单服务数据一致性
            //对账主要思路：扫描两个系统的数据，看那边漏了，就给补上
            //扫描最近的seckillOrder的记录，再根据每条记录的数据去订单服务找对应记录（可以根据outOrderId查找）
        }
    }

    @Builder
    public static class CreateSeckillOrderMessage {
        public long userId;
        public long acitvityId;
    }
}
